/**
 * @file Socket.hpp
 * @author 1102403684@qq.com
 * @brief 封装 服务端/客户端的网络套接字操作
 * @version 0.1
 * @date 2024-10-03
 *
 * @copyright Copyright (c) 2024
 *
 */
#pragma once
#define MAX_LISTEN 128

#include <cstdint>
#include <string>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <unistd.h>
#include <fcntl.h>


#include "logger.hpp"

class Socket
{
public:
    Socket() : sockfd_(-1) {}
    Socket(int fd) : sockfd_(fd) {}
    bool create();
    bool bind(const std::string ip, uint16_t port);
    bool listen();
    int accept();
    bool connect(const std::string ip, uint16_t port);

    ssize_t recv(void *buf, size_t len, int flag = 0);
    ssize_t recv_nonblock(void *buf, size_t len);
    ssize_t send(const void *buf, size_t len, int flag = 0);
    ssize_t send_nonblock(const void *buf, size_t len);
    void close();

    bool create_server_socket(uint16_t port, bool is_nonblock = false);
    bool create_client_socket(const std::string ip, uint16_t port);

    void set_reuse_addr();
    void set_nonblock();

    int get_fd() {return sockfd_;}

private:
    int sockfd_;
};

/*--------------------------implement----------------------------*/

bool Socket::create()
{
    sockfd_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sockfd_ < 0)
    {
        LOG_ERROR("创建套接字失败");
        return false;
    }
    return true;
}

bool Socket::bind(const std::string ip, uint16_t port)
{
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    //port一定不要忘记转换成网络字节序，否则可能对使用这个Socket.hpp的客户端并没有异常，
    //但使用命令查询端口是否监听时会出现错误(因为是按照不同的字节序)
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr(ip.c_str());
    // addr.sin_addr.s_addr = INADDR_ANY;
    socklen_t len = sizeof(struct sockaddr_in);
    int ret = ::bind(sockfd_, (const sockaddr *)&addr, len);
    if (ret < 0)
    {
        LOG_ERROR("绑定地址信息失败");
        return false;
    }
    return true;
}

bool Socket::listen()
{
    int ret = ::listen(sockfd_, MAX_LISTEN);
    if (ret < 0)
    {
        LOG_ERROR("监听失败");
        return false;
    }
    return true;
}

int Socket::accept()
{
    int newfd = ::accept(sockfd_, NULL, NULL);
    if (newfd < 0)
    {
        LOG_ERROR("接受新连接时失败");
        return -1;
    }
    return newfd;
}

bool Socket::connect(const std::string ip, uint16_t port)
{
    sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr(ip.c_str());
    socklen_t len = sizeof(struct sockaddr_in);
    int ret = ::connect(sockfd_, (const sockaddr *)&addr, len);
    if (ret < 0)
    {
        LOG_ERROR("连接失败");
        return false;
    }
    return true;
}

/**
 * @brief
 *
 * @param buf
 * @param len
 * @param flag
 * @return ssize_t 读取数据的大小
 * 0 表示EAGAIN(缓冲区中没有数据，在非阻塞读取时会出现) EINTR(socket的阻塞等待被信号打断)
 * -1 表示对端关机
 */
ssize_t Socket::recv(void *buf, size_t len, int flag)
{
    ssize_t size = ::recv(sockfd_, buf, len, flag);
    if (size <= 0)
    {
        if (errno == EAGAIN || errno == EINTR)
            return 0;
        else
            return -1;
    }
    return size;
}

ssize_t Socket::recv_nonblock(void *buf, size_t len)
{
    return recv(buf, len, MSG_DONTWAIT);
}


/**
 * @brief 
 * 
 * @param buf 
 * @param len 
 * @param flag 
 * @return ssize_t -1表示发送错误且不原谅    若为0则表示资源暂时不可用或被信号打断    若大于0表示实际的发送字节
 */
ssize_t Socket::send(const void *buf, size_t len, int flag)
{
    ssize_t size = ::send(sockfd_, buf, len, flag);
    if (size < 0)
    {
        if(errno == EAGAIN || errno == EINTR){
            return 0;
        }
        LOG_ERROR("发送数据失败");
        return -1;
    }
    return size; // 实际发送的数据长度，不一定全部发完
}

ssize_t Socket::send_nonblock(const void *buf, size_t len)
{
    if(len == 0) return 0;
    
    return send(buf, len, MSG_DONTWAIT);
}

void Socket::close()
{
    if (sockfd_ != -1)
    {
        ::close(sockfd_);
        sockfd_ = -1;
    }
}

bool Socket::create_server_socket(uint16_t port, bool is_nonblock)
{
    if (create() == false)
        return false;
    if(is_nonblock)
        set_nonblock();
    if (bind("0.0.0.0", port) == false)
        return false;
    if (listen() == false)
        return false;
    set_reuse_addr();
    LOG_DEBUG("创建服务器套接字成功 端口 {}", port);
    return true;
}
bool Socket::create_client_socket(const std::string ip, uint16_t port)
{
    if (create() == false)
        return false;
    if (connect(ip, port) == false)
        return false;
    return true;
}

void Socket::set_reuse_addr()
{
    int val = 1;
    setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR, (void*)&val, sizeof(int));
    val = 1;
    setsockopt(sockfd_, SOL_SOCKET, SO_REUSEPORT, (void*)&val, sizeof(int));
}


void Socket::set_nonblock()
{
    int flag = fcntl(sockfd_, F_GETFL, 0);
    fcntl(sockfd_, F_SETFL, flag | O_NONBLOCK);
}